package com.acmen.easyapi.builder;

import com.acmen.easyapi.annotation.ReqParam;
import com.acmen.easyapi.generator.DataGenerator;
import com.acmen.easyapi.generator.DataGeneratorFactory;
import com.acmen.easyapi.strategy.ReqParamStrategy;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.acmen.easyapi.annotation.Param;
import com.acmen.easyapi.model.RequestData;
import com.acmen.easyapi.util.ClassUtil;
import com.acmen.easyapi.util.ParamNameDiscoverer;
import com.acmen.easyapi.util.StringUtil;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author acmen
 * 请求数据构建器
 */
@Component
public class RequestDataBuilder extends ApiDataBuilder {
    @Resource
    private DataGeneratorFactory dataGeneratorFactory;
    @Resource
    private ReqParamStrategy reqParamStrategy;

    public RequestData buildData(Method method,List<List<Annotation>> parameterAnnotations) {
         // 创建一个 RequestData 对象
        RequestData requestData = new RequestData();
        // 使用 ParamNameDiscoverer 类的 getParameterNames 方法获取参数的名称，并存储在 parameterNames 数组中
        String[] parameterNames = ParamNameDiscoverer.getParameterNames(method);
        // 获取方法的参数类型，并存储在 parameterTypes 数组中
        Type[] parameterTypes = method.getParameterTypes();
        // 获取方法参数的通配符类型，并存储在 genericParameterTypes 数组中
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        // 使用 AnnotatedElementUtils 类的 findMergedAnnotation 方法查找 RequestMapping 注解，并存储在 reqMapping 对象中
        RequestMapping reqMapping = AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class);
        // 调用 buildHeaderData 方法构建请求头数据，并存储在 headerData 对象中
        JSONObject headerData = buildHeaderData(reqMapping, parameterNames, parameterTypes, parameterAnnotations);
        // 如果 headerData 不为空，则将请求头数据设置到 requestData 对象中
        if (headerData != null) {
            requestData.setHeaders(headerData);
        }
        // 调用 buildPathVariableData 方法构建路径参数数据，并存储在 pathVariableData 对象中
        JSONObject pathVariableData = buildPathVariableData(parameterNames, parameterTypes, parameterAnnotations);
        // 如果 pathVariableData 不为空，则将路径参数数据设置到 requestData 对象中
        if (pathVariableData != null) {
            requestData.setPathVariables(pathVariableData);
        }
        // 调用 buildParamData 方法构建参数数据，并存储在 paramData 对象中
        JSONObject paramData = buildParamData(parameterNames, genericParameterTypes, parameterAnnotations);
        // 如果 paramData 不为空，则将参数数据设置到 requestData 对象中
        if (paramData != null) {
            requestData.setParams(paramData);
        }
        // 调用 buildBodyData 方法构建请求体数据，并存储在 bodyData 对象中
        JSON bodyData = buildBodyData(genericParameterTypes, parameterAnnotations);
        // 如果 bodyData 不为空，则将请求体数据设置到 requestData 对象中
        if (bodyData != null) {
            requestData.setBody(bodyData);
        }
        // 返回 requestData 对象
        return requestData;

    }

    private JSONObject buildHeaderData(RequestMapping reqMapping,String[] parameterNames,Type[] parameterTypes,List<List<Annotation>> parameterAnnotations) {
        // 创建一个JSONObject对象用于存储请求头数据
        JSONObject headerData = new JSONObject();
        // 遍历参数类型数组
        for (int i = 0; i < parameterTypes.length; i++) {
            // 获取当前参数的类型
            Type parameterType = parameterTypes[i];
            // 获取当前参数的注解信息
            RequestHeader requestHeader = getAnnotation(parameterAnnotations.get(i), RequestHeader.class);
            ReqParam reqParam = getAnnotation(parameterAnnotations.get(i), ReqParam.class);

            // 如果参数有RequestHeader注解
            if (requestHeader != null) {
                // 如果注解的value值为空，则使用注解的name值作为参数名，否则使用注解的value值作为参数名
                String paramName = StringUtil.isEmpty(requestHeader.value()) ? requestHeader.name() : requestHeader.value();
                // 如果参数名为空，则使用参数索引作为参数名
                if(StringUtil.isEmpty(paramName)){
                    paramName = parameterNames[i];
                }
                // 如果参数有ReqParam注解且ReqParam注解的example值不为空，则将example值放入JSONObject对象中
                if(reqParam!=null&&StringUtil.isNotEmpty(reqParam.example())){
                    headerData.put(paramName,reqParam.example());
                }else{
                    // 否则，根据参数类型生成数据，并将数据放入JSONObject对象中
                    Object obj = buildData(parameterType,new HashMap<>(),null);
                    headerData.put(paramName,obj);
                }
            }
        }

        // 遍历请求映射中的headers，将header按照"key=value"的格式放入JSONObject对象中
        for(String header : reqMapping.headers()){
            String[] headers = header.split("=");
            if(headers.length>=2){
                headerData.put(headers[0],headers[1]);
            }
        }
        // 如果请求映射中的consumes不为空，则将第一个consumes值放入JSONObject对象中的"Content-Type"键中
        if(reqMapping.consumes().length>0){
            headerData.put("Content-Type",reqMapping.consumes()[0]);
        }else{
            // 否则，遍历参数类型数组，如果参数有RequestBody注解，则将"Content-Type"键设置为"application/json"
            for (int i = 0; i < parameterTypes.length; i++) {
                RequestBody requestBody = getAnnotation(parameterAnnotations.get(i), RequestBody.class);
                if(requestBody!=null){
                    headerData.put("Content-Type","application/json");
                }
            }
        }
        // 返回请求头数据的JSONObject对象
        return headerData;

    }


    /**
     * 构造路径变量数据
     * @param parameterNames
     * @param parameterTypes
     * @param parameterAnnotations
     * @return
     */
    private JSONObject buildPathVariableData(String[] parameterNames,Type[] parameterTypes,List<List<Annotation>> parameterAnnotations) {
        JSONObject pathVariableData = new JSONObject();
        for (int i = 0; i < parameterTypes.length; i++) {
            Type parameterType = parameterTypes[i];
            PathVariable pathVariable = getAnnotation(parameterAnnotations.get(i), PathVariable.class);
            ReqParam reqParam = getAnnotation(parameterAnnotations.get(i), ReqParam.class);
            if (pathVariable != null) {
                String paramName = StringUtil.isEmpty(pathVariable.value()) ? pathVariable.name() : pathVariable.value();
                if(StringUtil.isEmpty(paramName)){
                    paramName = parameterNames[i];
                }
                if(reqParam!=null&&StringUtil.isNotEmpty(reqParam.example())){
                    pathVariableData.put(paramName,reqParam.example());
                }else{
                    Object obj = buildData(parameterType,new HashMap<>(),null);
                    pathVariableData.put(paramName,obj);
                }
            }
        }
        return pathVariableData.isEmpty()?null:pathVariableData;
    }


    private JSONObject buildParamData(String[] parameterNames,Type[] genericParameterTypes,List<List<Annotation>> parameterAnnotations) {
        JSONObject paramData = new JSONObject();
        for (int i=0;i<genericParameterTypes.length;i++) {
            Type parameterType = genericParameterTypes[i];
            //发现controller方法中有DTO类，那从DTO类中解释生成文档
            if (ClassUtil.isDtoType(parameterType)) {
                RequestBody requestBody = getAnnotation(parameterAnnotations.get(i), RequestBody.class);
                if(requestBody==null){
                    Object object = buildData(genericParameterTypes[i], new HashMap<>(), null);
                    if(object!=null&&object instanceof JSONObject){
                        paramData.putAll((JSONObject)object);
                    }
                }
            }
        }

        for (int i = 0; i < genericParameterTypes.length; i++) {
            Type parameterType = genericParameterTypes[i];
            RequestParam requestParam = getAnnotation(parameterAnnotations.get(i), RequestParam.class);
            ReqParam reqParam = getAnnotation(parameterAnnotations.get(i), ReqParam.class);
            if (requestParam != null) {
                String paramName = StringUtil.isEmpty(requestParam.value()) ? requestParam.name() : requestParam.value();
                if(StringUtil.isEmpty(paramName)){
                    paramName = parameterNames[i];
                }
                if(paramData.containsKey(paramName)){
                    continue;
                }
                if(reqParam!=null&&StringUtil.isNotEmpty(reqParam.example())){
                    paramData.put(paramName,reqParam.example());
                }else{
                    Object obj = buildData(parameterType,new HashMap<>(),null);
                    paramData.put(paramName,obj);
                }
            }
        }
        return paramData.isEmpty()?null:paramData;
    }

    private JSON buildBodyData(Type[] genericParameterTypes, List<List<Annotation>> parameterAnnotations) {
        for (int i=0;i<genericParameterTypes.length;i++) {
            Type parameterType = genericParameterTypes[i];
            //发现controller方法中有DTO类，那从DTO类中解释生成文档
            if (ClassUtil.isDtoType(parameterType)) {
                RequestBody requestBody = getAnnotation(parameterAnnotations.get(i), RequestBody.class);
                if(requestBody!=null){
                    Object object = buildData(genericParameterTypes[i], new HashMap<>(), null);
                    return object!=null?(JSON) object:null;
                }
            }
        }
        return null;
    }

    protected JSONObject classToJson(Class clazz, Map<TypeVariable, Type> typeMap) {
        JSONObject jsonObject = new JSONObject();
        List<Field> fields = ClassUtil.getFields(clazz);
        for (Field field : fields) {
            Param param = reqParamStrategy.getParam(clazz,field);
            if(param == null){
                continue;
            }
            DataGenerator dataGenerator = dataGeneratorFactory.getDataGenerator(field, param);
            if (dataGenerator != null) {
                Object randomData = dataGenerator.generate(field, param);
                if(randomData != null){
                    jsonObject.put(field.getName(), randomData);
                }else{
                    String example = param.example();
                    Object o = buildData(field.getGenericType(), typeMap, example);
                    if (StringUtil.isNotEmpty(o)) {
                        jsonObject.put(field.getName(), o);
                    }
                }
            } else {
                String example = param.example();
                Object o = buildData(field.getGenericType(), typeMap, example);
                if (StringUtil.isNotEmpty(o)) {
                    jsonObject.put(field.getName(), o);
                }
            }
        }
        return jsonObject;
    }

    /**
     * 获取注解类型
     *
     * @param annotations
     * @param clazz
     * @param <T>
     * @return
     */
    protected <T> T getAnnotation(List<Annotation> annotations, Class<T> clazz) {
        for (Annotation annotation : annotations) {
            if (annotation.annotationType().equals(clazz)) {
                return (T) annotation;
            }
        }
        return null;
    }
}
